#
# LuckyBot4, a python IRC bot
# (c) Copyright 2008 by Lucas van Dijk
# http://www.return1.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# $Id: __init__.py 77 2009-06-06 13:00:28Z luckyluke56 $
#

try:
	import pygtk
	pygtk.require('2.4')
except:
	pass

from gettext import gettext as _
import gtk
import gobject
import gtk.glade

import socket

from luckybot.luckynet.connection.glib import GlibConnection
from luckybot.bot import irc
from luckybot.views.gui.config import ConfigWindow
from luckybot.views.gui import dialogs
import luckybot.views.base
import luckybot.path


gtk.gdk.threads_init()

# Make tab close buttons smaller
gtk.rc_parse_string("""
	style "thinWidget" {
		xthickness = 0
		ythickness = 0
	}
	widget "*.tabCloseButton" style "thinWidget"
	""")

class ServerTab(gtk.EventBox):
	"""
		A Tab widget with a close button
	"""

	def __init__(self, title, page, nb, main_window):
		gtk.EventBox.__init__(self)
		self.set_visible_window(False)

		self.connect('button_press_event', self.on_button_press)

		self.nb = nb
		self.title = title
		self.page = page
		self.main_window = main_window
		self.tips = gtk.Tooltips()

		self.hbox = hbox = gtk.HBox(False, 2)
		self.add(hbox)

		self.label = gtk.Label(title)
		hbox.pack_start(self.label, False, False)

		self.button = btn = gtk.Button()
		btn.set_name('tabCloseButton')
		btn.set_relief(gtk.RELIEF_NONE)
		btn.set_focus_on_click(False)
		btn.connect('clicked', self.do_close)
		btn.connect('button_press_event', self.on_button_press)
		self.tips.set_tip(btn, _("Close Tab"))
		image = gtk.Image()
		image.set_from_stock('gtk-close', gtk.ICON_SIZE_MENU)
		btn.add(image)
		hbox.pack_end(btn, False, False)

		self.show_all()

	def on_button_press(self, widget, event):
		"""
			Called when the user clicks on the tab
		"""
		pass

	def do_close(self, *args):
		"""
			Called when the user clicks the close button on the tab
		"""

		# Display confirmation dialog
		dialog = gtk.MessageDialog(self.main_window, type=gtk.MESSAGE_WARNING,
			buttons=gtk.BUTTONS_YES_NO, message_format=
				_("Are you sure you want to close this tab?"))

		response = dialog.run()
		dialog.hide()

		if response == gtk.RESPONSE_YES:
			if self.page.connected:
				self.page.disconnect()

			num = self.nb.page_num(self.page)
			self.nb.remove_page(num)

class ServerTabpage(gtk.VBox):
	"""
		Custom widget for the notebook
		It contains a textview, and an entry to send raw commands
	"""

	__gsignals__ = {
		'on-connection-close': (gobject.SIGNAL_RUN_LAST, None, ())
	}

	def __init__(self, server_config, view):
		gtk.VBox.__init__(self, False, 5)
		self.set_border_width(10)

		# Create some widgets
		scrolled_window = gtk.ScrolledWindow()
		scrolled_window.set_shadow_type(gtk.SHADOW_ETCHED_IN)
		scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

		# Create text view
		self.textview = gtk.TextView()
		self.textview.set_editable(False)
		scrolled_window.add(self.textview)

		self.pack_start(scrolled_window)

		# Create command text entry
		hbox = gtk.HBox(False, 5)
		self.textentry = gtk.Entry()
		self.textentry.connect('activate', self.on_button_click)

		button = gtk.Button(_("Send"))
		button.connect('clicked', self.on_button_click)

		hbox.pack_start(self.textentry)
		hbox.pack_start(button, False, False)

		self.pack_start(hbox, False, False)

		# Save connection state
		self.server_config = server_config
		self.connected = False
		self.client = None
		self.view = view

		self.show_all()

	def open_connection(self, config):
		"""
			Opens the connection to a specified IRC Server

			If called without paramaters, the hostname and port is read
			from the settings file.

			@type config: tuple
			@param config: Tuple containing all server config in following order:
				hostname, port, nickname, password, channels
		"""

		host, port, nickname, password, channels, send_as_notice = config

		connection = GlibConnection(socket.AF_INET, socket.SOCK_STREAM)
		protocol = irc.Client(self.view.bot, nickname, password, channels.split(','), send_as_notice, self.view.bot.settings.getboolean('Bot', 'invisible'))
		protocol.connect('irc-event', self.view.bot.on_message)
		protocol.connect('data-sent', self.on_data_sent)
		protocol.connect('data-received', self.on_data_received)
		protocol.connect('closed', self.on_client_close)

		connection.set_protocol(protocol)
		connection.open((host, port))
		connection.setblocking(0)

		self.client = protocol

	def connect_to_server(self):
		"""
			Connect to the server
		"""

		if (not 'nickname' in self.server_config or
				not 'hostname' in self.server_config):
			raise ValueError, ('Incomplete configuration, need at least' +
				' hostname and nickname')

		try:
			port = int(self.server_config['port'])
		except:
			port = 6667

		channels = self.server_config['channels'] if 'channels' in self.server_config else ''
		password = self.server_config['password'] if 'password' in self.server_config else ''
		send_as_notice = True if 'send_as_notice' in self.server_config and self.server_config['send_as_notice'].lower() == 'true' else False

		config = (self.server_config['hostname'], port, self.server_config['nickname'],
			password, channels, send_as_notice)

		try:
			self.open_connection(config)
		except socket.error, e:
			# Error!
			dialog = gtk.MessageDialog(self.view.window, type=gtk.MESSAGE_WARNING,
				buttons=gtk.BUTTONS_OK, message_format=_("Could not connect to server"))
			dialog.format_secondary_text(str(e[1]))

			dialog.run()
			dialog.hide()

			return

		self.connected = True

	def disconnect(self):
		"""
			Disconnect this server
		"""

		if hasattr(self, 'client') and self.client != None:
			self.client.connection.close()
			self.connected = False
			del self.client
			self.emit('on-connection-close')

	def on_button_click(self, widget = None, event = None):
		"""
			Called when the user clicks the 'Send' button
		"""

		text = self.textentry.get_text()

		if text and self.connected:
			self.client.send(text)
			self.textentry.set_text('')

	def add_log(self, text):
		"""
			Add a line to the log
		"""
		buffer = self.textview.get_buffer()

		buffer.insert(buffer.get_end_iter(), text)
		self.textview.scroll_to_mark(buffer.get_insert(), 0)

	def get_log(self):
		"""
			Gets the complete log string
		"""

		buffer = self.textview.get_buffer()

		return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())

	def on_data_received(self, protocol, data):
		"""
			Called when data arrives
		"""

		self.add_log("<<< %s" % data)

	def on_data_sent(self, protocol, data):
		"""
			Called when data is sent, sends the data to our view
		"""

		self.add_log(">>> %s" % data)

	def on_client_close(self, protocol):
		"""
			Called when the socket is closed
		"""

		self.connected = False
		if hasattr(self, 'client'):
			del self.client

		self.add_log(_("Connection Closed"))
		self.emit('on-connection-close')


class GUIView(luckybot.views.base.BaseView, gobject.GObject):
	"""
		This class represents the main LuckyBot window
	"""

	def __init__(self):
		"""
			Loads and shows the window
		"""

		gobject.GObject.__init__(self)

		self.xml = gtk.glade.XML(luckybot.path.get_base_path('data', 'glade', 'mainwindow.glade'), 'mainWindow', 'luckybot')
		self.window = self.xml.get_widget('mainWindow')

		self.window.set_title(_("Lucky Bot"))

		self.xml.signal_autoconnect(self)
		self.window.show()
		self.window.connect('destroy', self.on_quit)

	def set_defaults(self):
		"""
			Loads default servers, and adds a tab page for each server
		"""
		servers = self.bot.get_servers()

		for server_config in servers:
			self.open_tab(server_config)

	def open_tab(self, server_config):
		"""
			Open a new server tab
		"""

		notebook = self.xml.get_widget('mainTabs')
		page = ServerTabpage(server_config, self)
		page.connect('on-connection-close', self.on_close)

		title = "%s:%s" % (server_config['hostname'], server_config['port'])
		label = ServerTab(title, page, notebook, self.window)

		notebook.append_page(page, label)
		notebook.set_current_page(-1)


	def on_close(self, page):
		"""
			Called when the socket is closed
		"""

		notebook = self.xml.get_widget('mainTabs')

		if page == notebook.get_nth_page(notebook.get_current_page()):
			self.toggle_connect_button(False)

	def toggle_connect_button(self, connected):
		"""
			This function changes the 'connect' menu item and 'connect' button
			to 'disconnect' when they click it and reverse
		"""

		if connected:
			for child in self.xml.get_widget('menuConnect').get_children():
				if isinstance(child, gtk.AccelLabel):
					child.set_text(_('Disconnect'))

			self.xml.get_widget('menuConnect').set_image(gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_MENU))

			self.xml.get_widget('btnConnect').set_stock_id(gtk.STOCK_DISCONNECT)
		else:
			for child in self.xml.get_widget('menuConnect').get_children():
				if isinstance(child, gtk.AccelLabel):
					child.set_text(_('Connect'))

			self.xml.get_widget('menuConnect').set_image(gtk.image_new_from_stock(gtk.STOCK_CONNECT, gtk.ICON_SIZE_MENU))

			self.xml.get_widget('btnConnect').set_stock_id(gtk.STOCK_CONNECT)


	def do_connect(self, widget = None, event = None):
		"""
			Connects to the given server
		"""

		# get current page
		notebook = self.xml.get_widget('mainTabs')
		page = notebook.get_nth_page(notebook.get_current_page())

		if page.connected and hasattr(page, 'client'):
			page.disconnect()
			self.toggle_connect_button(page.connected)
		else:
			page.connect_to_server()
			self.toggle_connect_button(page.connected)

	def do_save_log(self, widget = None, event = None):
		"""
			Saves the log to a file
		"""

		# Open up a filechooser dialog
		dialog = gtk.FileChooserDialog(_("Save log"), self.window, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK))
		response = dialog.run()

		if response == gtk.RESPONSE_OK:
			# Get current page
			notebook = self.xml.get_widget('mainTabs')
			page = notebook.get_nth_page(notebook.get_current_page())

			# Open the file
			file = open(dialog.get_filename(), 'w')
			file.write(page.get_log())

		dialog.hide()

	def on_quit(self, widget = None, event = None):
		"""
			Quits the bot and window
		"""
		self.window.hide()
		gtk.main_quit()

	def on_item_preferences(self, widget = None, event = None):
		"""
			shows the config window
		"""

		config = ConfigWindow(self.bot)
		response = config.run()

		if response == gtk.RESPONSE_OK:
			config.save()

		config.on_quit()

	def on_toolAddServer_clicked(self, button = None, event = None):
		"""
			Shows a dialog for connecting to a server
		"""

		dialog = dialogs.AddServerDialog(self.window)
		response = dialog.run()

		error = False

		if response == gtk.RESPONSE_OK:
			hostname = dialog.txtHostname.get_text()
			if hostname.find(':') != -1:
				parts = hostname.split(':', 1)
				try:
					hostname = parts[0]
					port = int(parts[1])
				except:
					error = True
					message = gtk.MessageDialog(dialog, type=gtk.MESSAGE_ERROR,
						buttons=gtk.BUTTONS_OK, message_format=
						_("Invalid Hostname"))
					message.run()
					message.hide()
			else:
				port = 6667

			if not error:
				server_config = {
					'hostname': hostname,
					'port': str(port),
					'nickname': dialog.txtNickname.get_text(),
					'password': dialog.txtPassword.get_text(),
					'channels': dialog.txtChannels.get_text()
				}

				self.open_tab(server_config)
				dialog.hide()
		else:
			dialog.hide()

	def on_tabpage_change(self, notebook, page, page_num):
		"""
			Called when the notebook page changes

			We check if that page is connected, and update the connect
			buttons
		"""

		page = self.xml.get_widget('mainTabs').get_nth_page(page_num)

		self.toggle_connect_button(page.connected)

	def error(self, data):
		"""
			Called when an error occurs

			@type data: string
			@param data: The data to add
		"""

		dialog = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, data)
		dialog.run()
		dialog.hide()

	def start(self):
		"""
			Starts the view
		"""

		# Set Defaults
		self.set_defaults()

		gtk.main()
